/**
 * Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.betterjr.common.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.betterjr.common.config.ParamNames;
import com.betterjr.common.data.KeyAndValueObject;
import com.betterjr.common.exception.BettjerIOException;
import com.betterjr.modules.sys.service.SysConfigService;

/**
 * 文件操作工具类 实现oss文件的创建、删除、复制，读取
 */
public class FileUtils extends org.apache.commons.io.FileUtils {

    private static Logger log = LoggerFactory.getLogger(FileUtils.class);

    public static long ONE_KB = 1024;
    public static long ONE_MB = ONE_KB * 1024;
    public static long ONE_GB = ONE_MB * 1024;
    public static long ONE_TB = ONE_GB * 1024;
    public static long ONE_PB = ONE_TB * 1024;

    public static Set<String> SupportedUploadFileType = new HashSet<String>();
    static {
        SupportedUploadFileType.add("jpg");
        SupportedUploadFileType.add("jpeg");
        SupportedUploadFileType.add("png");
        SupportedUploadFileType.add("gif");
        SupportedUploadFileType.add("doc");
        SupportedUploadFileType.add("docx");
        SupportedUploadFileType.add("pdf");
        SupportedUploadFileType.add("xls");
        SupportedUploadFileType.add("xlsx");
        SupportedUploadFileType.add("zip");
        SupportedUploadFileType.add("rar");
        SupportedUploadFileType.add("ftl");
        SupportedUploadFileType.add("txt");
    }

    public static boolean isSupportedUploadFileType(final String type) {
        if (StringUtils.isBlank(type)) {
            return false;
        }
        return SupportedUploadFileType.contains(type.trim().toLowerCase());
    }

    public static String getHumanReadableFileSize(final Long fileSize) {
        if (fileSize == null) return null;
        return getHumanReadableFileSize(fileSize.longValue());
    }

    public static String getHumanReadableFileSize(final long fileSize) {
        if (fileSize < 0) {
            return String.valueOf(fileSize);
        }
        String result = getHumanReadableFileSize(fileSize, ONE_PB, "PB");
        if (result != null) {
            return result;
        }

        result = getHumanReadableFileSize(fileSize, ONE_TB, "TB");
        if (result != null) {
            return result;
        }
        result = getHumanReadableFileSize(fileSize, ONE_GB, "GB");
        if (result != null) {
            return result;
        }
        result = getHumanReadableFileSize(fileSize, ONE_MB, "MB");
        if (result != null) {
            return result;
        }
        result = getHumanReadableFileSize(fileSize, ONE_KB, "KB");
        if (result != null) {
            return result;
        }
        return String.valueOf(fileSize) + "B";
    }

    private static String getHumanReadableFileSize(final long fileSize, final long unit, final String unitName) {
        if (fileSize == 0) return "0";

        if (fileSize / unit >= 1) {
            final double value = fileSize / (double) unit;
            final DecimalFormat df = new DecimalFormat("######.##" + unitName);
            return df.format(value);
        }
        return null;
    }

    public static File getRealFile(final String anPath) {
        String tmpStr = null;
        if (StringUtils.isNotBlank(anPath)) {
            tmpStr = anPath.replaceAll("\\.\\.", "");
        }
        if (tmpStr != null) {
            final File ff = new File(tmpStr);
            if (ff.exists() && ff.isFile()) {
                System.out.println(tmpStr);
                return ff;
            }
        }
        return null;
    }

    /**
     * 根据指定的父文件路径，创建文件路径
     * 
     * @param anParentPath
     *            指定的父文件路径
     * @return
     */
    public static KeyAndValueObject findFilePathWithParent(final String anParentPath) {
        // 得到上传服务器的路径
        final String basePath = (String) SysConfigService.getObject(ParamNames.OPENACCO_FILE_DOWNLOAD_PATH);

        return findFilePathWithParent(anParentPath, basePath);
    }

    /**
     * 根据指定的父文件路径，创建文件路径
     * 
     * @param anParentPath
     *            指定的父文件路径
     * @return
     */
    public static KeyAndValueObject findFilePathWithParent(final String anParentPath, final String basePath) {
        // 得到上传服务器的路径
        final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
        final String format = formatter.format(new Date());
        String workPath = null;
        if (StringUtils.isBlank(anParentPath)) {
            workPath = basePath + "/" + format;
        } else {
            workPath = basePath + "/" + anParentPath + "/" + format;
        }
        final File file = new File(workPath);

        // 如果文件夹不存在则创建
        if (file.exists()) {

            // 如果是文件，则将文件做重命名
            if (file.isFile()) {
                file.renameTo(new File(workPath + ".rename"));
                file.mkdirs();
            }
        } else {
            // 不存在，就创将目录
            file.mkdirs();
        }
        // 得到上传的文件的文件名
        final String fileName = UUIDUtils.uuid();
        final String fileAbsoPath = workPath + "/" + fileName;

        // 保存在系统中的相对路径
        String saveUrl = null;
        if (StringUtils.isNotBlank(anParentPath)) {
            saveUrl = "/" + anParentPath;
        } else {
            saveUrl = "";
        }
        saveUrl = saveUrl + "/" + format + "/" + fileName;

        return new KeyAndValueObject(saveUrl, new File(fileAbsoPath));
    }

    /**
     * 获得文件的扩展名
     * @param anFileName
     * @return
     */
    public static String extractFileExt(final String anFileName) {
        if (StringUtils.isNotBlank(anFileName)) {
            if (anFileName.contains(".")) {
                return anFileName.substring(anFileName.lastIndexOf(".") + 1);
            }
        }
        return "bin";
    }

    /**
     * Returns the byte [] content of the specified file.
     *
     * @param file
     * @return the byte content of the file
     */
    public static byte[] readContent(final File file) {
        final byte[] buffer = new byte[(int) file.length()];
        BufferedInputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            is.read(buffer, 0, buffer.length);
        }
        catch (final Throwable t) {
            System.err.println("Failed to read byte content of " + file.getAbsolutePath());
            t.printStackTrace();
        }
        finally {
            if (is != null) {
                try {
                    is.close();
                }
                catch (final IOException ioe) {
                    System.err.println("Failed to close file " + file.getAbsolutePath());
                    ioe.printStackTrace();
                }
            }
        }
        return buffer;
    }

    /**
     * Returns the string content of the specified file.
     *
     * @param file
     * @param lineEnding
     * @return the string content of the file
     */
    public static String readContent(final File file, final String lineEnding) {
        final StringBuilder sb = new StringBuilder();
        InputStreamReader is = null;
        BufferedReader reader = null;
        try {
            is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
            reader = new BufferedReader(is);
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                if (lineEnding != null) {
                    sb.append(lineEnding);
                }
            }
        }
        catch (final Throwable t) {
            System.err.println("Failed to read content of " + file.getAbsolutePath());
            t.printStackTrace();
        }
        finally {
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (final IOException ioe) {
                    System.err.println("Failed to close file " + file.getAbsolutePath());
                    ioe.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                }
                catch (final IOException ioe) {
                    System.err.println("Failed to close file " + file.getAbsolutePath());
                    ioe.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    public static String removeFileExt(final String anFile) {
        final int pos = anFile.lastIndexOf(".");
        if (pos > 0) {
            return anFile.substring(0, pos);
        } else {
            return anFile;
        }
    }

    /**
     * 替换文件的扩展名
     * 
     * @param anFile
     * @param anExt
     * @return
     */
    public static File replaceFileExt(final File anFile, final String anExt) {
        String tmpStr = anFile.getName();
        final int pos = tmpStr.lastIndexOf(".");
        if (pos > 0) {
            tmpStr = tmpStr.substring(0, pos);
        }
        tmpStr = tmpStr.concat(".").concat(anExt);

        return new File(anFile.getParent().concat(File.separator).concat(tmpStr));
    }

    /**
     * 解压GZ压缩文件，如果扩展名是GZ结束，则文件名去掉.GZ；如果不是GZ结束，则文件名字相同。
     * 
     * @param anFile
     * @param anDirPath
     * @return
     */
    public static File unGzipFile(final File anFile, String anDirPath) {
        if (anDirPath.endsWith(File.separator) == false) {
            anDirPath = anDirPath + File.separator;
        }
        File destFile;
        if (anFile.getName().endsWith(".gz")) {
            destFile = new File(anDirPath + anFile.getName().substring(0, anFile.getName().length() - 3));
        } else {
            destFile = new File(anDirPath + anFile.getName());
        }
        GZIPInputStream gzin = null;
        try {
            gzin = new GZIPInputStream(new FileInputStream(anFile));
            copyInputStreamToFile(gzin, destFile);
        }
        catch (final IOException ex) {
            ex.printStackTrace();
        }
        finally {
            IOUtils.closeQuietly(gzin);
        }
        return destFile;
    }

    /**
     * 解压缩ZIP文件，将ZIP文件里的内容解压到descFileName目录下
     * 
     * @param zipFileName
     *            需要解压的ZIP文件
     * @param descFileName
     *            目标文件
     */
    public static List<File> unZipFiles(final File zipFileName, final String descFileName) {
        String descFileNames = descFileName;
        if (!descFileNames.endsWith(File.separator)) {
            descFileNames = descFileNames + File.separator;
        }
        try {
            // 根据ZIP文件创建ZipFile对象
            final ZipFile zipFile = new ZipFile(zipFileName);
            ZipEntry entry = null;
            String entryName = null;
            String descFileDir = null;
            final byte[] buf = new byte[4096];
            final int readByte = 0;
            // 获取ZIP文件里所有的entry
            @SuppressWarnings("rawtypes")
            final Enumeration enums = zipFile.entries();
            // 遍历所有entry
            final List<File> data = new ArrayList<File>();
            while (enums.hasMoreElements()) {
                entry = (ZipEntry) enums.nextElement();
                // 获得entry的名字
                entryName = entry.getName();
                descFileDir = descFileNames + entryName;
                if (entry.isDirectory()) {
                    // 如果entry是一个目录，则创建目录
                    new File(descFileDir).mkdirs();
                    continue;
                } else {
                    // 如果entry是一个文件，则创建父目录
                    new File(descFileDir).getParentFile().mkdirs();
                }
                final File file = new File(descFileDir);
                copyInputStreamToFile(zipFile.getInputStream(entry), file);
                data.add(file);
            }
            zipFile.close();
            log.debug("文件解压成功!");
            return data;
        }
        catch (final Exception e) {
            log.warn("文件解压失败：" + e.getMessage());
            throw new BettjerIOException(12313, "文件解压失败：" + zipFileName, e);
        }
    }

}